#include "audiodecodeimpl.h"
#include "audioabstract.h"
AudioDecodeImpl::AudioDecodeImpl(AudioAbstract *event):
    audio_clock(0),
    m_pAStream(0),
    audio_buf_size(0),
    audio_buf_index(0),
    au_convert_ctx(0),
    out_buffer(0),
    m_pFrame(0),
    m_oAudioAbstract(event)
{
}

AudioDecodeImpl::~AudioDecodeImpl()
{

}

bool AudioDecodeImpl::Init(AVStream *stream)
{
    if(stream == 0 || stream->codec == 0)
        return false;

    AVCodec  * pCodecV = avcodec_find_decoder(stream->codec->codec_id);
    if ((pCodecV ==0) || (avcodec_open2(stream->codec, pCodecV,NULL) < 0))
        return false;

    m_pAStream  = stream;
    m_pFrame    = avcodec_alloc_frame();
    out_buffer  =(uint8_t *)av_malloc(192000*2);

    memset(&pkt_temp, 0, sizeof(AVPacket));
    memset(&pkt_cur, 0, sizeof(AVPacket));

    int len = audio_decode_frame();
    if( len > 0)
        audio_buf_size = len;

    if (au_convert_ctx == 0)
    {
        au_convert_ctx = swr_alloc();

        int64_t in_channel_layout = av_get_default_channel_layout(m_pAStream->codec->channels);//输入声道数

        au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, in_channel_layout, AV_SAMPLE_FMT_S16, m_pAStream->codec->sample_rate,
            in_channel_layout, m_pAStream->codec->sample_fmt, m_pAStream->codec->sample_rate, 0, NULL);

        swr_init(au_convert_ctx);

        if(m_oAudioAbstract)
            m_oAudioAbstract->start(stream->codec->sample_rate,stream->codec->channels,16);
    }
    return true;
}

bool AudioDecodeImpl::UnInit()
{
    if(m_pAStream == 0 || m_pAStream->codec == 0)
        return false;

    if(m_oAudioAbstract)
        m_oAudioAbstract->stop();

    avcodec_flush_buffers(m_pAStream->codec);
    av_freep(&m_pAStream->codec->extradata);
    m_pAStream->codec->extradata_size = 0;
    avcodec_close(m_pAStream->codec);

    clear_list();//清理队列

    if (au_convert_ctx)
    {
        swr_free(&au_convert_ctx);
        au_convert_ctx = 0;
    }
    if(m_pFrame)
    {
         av_free(m_pFrame);
         m_pFrame = 0;
    }

    if(out_buffer)
    {
        av_free(out_buffer);
        out_buffer = 0;
    }

    if (pkt_cur.data)
        av_free_packet(&pkt_cur);

    memset(&pkt_cur, 0, sizeof(AVPacket));
    memset(&pkt_temp, 0, sizeof(AVPacket));

    audio_buf_size  = 0;
    audio_buf_index = 0;
    audio_clock     = 0;
    m_pAStream      = 0;
    return true;
}

bool AudioDecodeImpl::receive_handler(const char *data, int size)
{
    AVPacket pktl;
    memcpy(&pktl,data,size);  
    push_audio_packet(pktl);
    return true;
}

unsigned int AudioDecodeImpl::read_data(char *data, unsigned int len)
{
    int lenOld = len;
    int len1, audio_size;
    while (len > 0)
    {
        if (audio_buf_index == audio_buf_size)
        {
            audio_size = audio_decode_frame();								//继续解码处理 并 返回buf大小

            if (audio_size < 0)
            {
                audio_buf_size = 1024;
                memset(out_buffer, 0 , audio_buf_size);
            }
            else
            {
                audio_buf_size = audio_size;
            }

            audio_buf_index = 0;
        }
        len1 = audio_buf_size - audio_buf_index;

        if (len1 > len)
            len1 = len;
        memcpy(data, (uint8_t *)out_buffer + audio_buf_index, len1);
        len -= len1;
        data += len1;
        audio_buf_index += len1;
    }
    return lenOld;
}

double AudioDecodeImpl::get_audio_clock()
{
    double pts;
    int hw_buf_size, bytes_per_sec, n;

    pts = audio_clock; // maintained in the audio thread
    hw_buf_size = audio_buf_size - audio_buf_index;
    bytes_per_sec = 0;
    n = m_pAStream->codec->channels * 2;
    if (m_pAStream)
    {
        bytes_per_sec = m_pAStream->codec->sample_rate * n;
    }
    if (bytes_per_sec)
    {
        pts -= (double)hw_buf_size / bytes_per_sec;
    }
    return pts;
}

int AudioDecodeImpl::get_queue_size()
{
    CCriticalAutoLock loAutoLock(oListSection);
    return m_AVPacketList.size();
}

void AudioDecodeImpl::clear_list()
{
    CCriticalAutoLock loAutoLock(oListSection);

    while(!m_AVPacketList.empty())
    {
        AVPacket aVpkt = m_AVPacketList.front();

        if(aVpkt.data)
            av_free_packet(&aVpkt);

        m_AVPacketList.pop();
    }
}

bool AudioDecodeImpl::pop_audio_packet(AVPacket &aVpkt1)
{
    CCriticalAutoLock loAutoLock(oListSection);

    if(!m_AVPacketList.empty())
    {
        aVpkt1 = m_AVPacketList.front();
        m_AVPacketList.pop();
        return  true;
    }else
    {
        return  false;
    }
}

void AudioDecodeImpl::push_audio_packet(AVPacket &aVpkt1)
{
    CCriticalAutoLock loAutoLock(oListSection);
    m_AVPacketList.push(aVpkt1);
}

int AudioDecodeImpl::audio_decode_frame()
{
	for (;;)
	{
		while (pkt_temp.size != 0)
		{
			int got_picture = 0;

			int retlen = avcodec_decode_audio4(m_pAStream->codec, m_pFrame, &got_picture, &pkt_temp);

			if (retlen < 0)
			{
				pkt_temp.size = 0;
				break;
			}

			pkt_temp.data += retlen;
			pkt_temp.size -= retlen;

			if (got_picture > 0)
			{
				memset(out_buffer, 0, 192000 * 2);
				if (au_convert_ctx == 0)
				{
					return -1;
				}
				swr_convert(au_convert_ctx, &out_buffer, 192000, (const uint8_t **)m_pFrame->data, m_pFrame->nb_samples);

                int n = 2 * m_pAStream->codec->channels;
                int out_buffer_size = av_samples_get_buffer_size(NULL, m_pAStream->codec->channels, m_pFrame->nb_samples, AV_SAMPLE_FMT_S16, 1);
                audio_clock += (double)out_buffer_size / (double)(n * m_pAStream->codec->sample_rate);

                return out_buffer_size;
			}
			else
			{
				continue;
			}
		}

        if (pkt_cur.data)
            av_free_packet(&pkt_cur);

        memset(&pkt_cur, 0, sizeof(AVPacket));
        memset(&pkt_temp, 0, sizeof(AVPacket));

		if (pop_audio_packet(pkt_cur) == false)  //没数据包了
		{
			return -1;
		}

		pkt_temp = pkt_cur;

		if (pkt_cur.pts != AV_NOPTS_VALUE)
		{
			audio_clock = av_q2d(m_pAStream->time_base) * pkt_cur.pts;
		}
	}
}

